/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef TERRAIN_HH_
#define TERRAIN_HH_

#include "carina/math.hh"
#include "carina/scene.hh"
#include "carina/image.hh"
#include "carina/resources.hh"

namespace Carina
{
class Terrain;

//! Data structure which encapsulates some of the Terrain quadtree node and the function which operate on them.
class QuadtreeNode
{
    QuadtreeNode    *m_Child[4];    //!< List of child nodes.
    float           m_Min,          //!< The height of the bottom face.
                    m_Max,          //!< The height of the top face.
                    m_Size;         //!< The width and the depth of the box.
    Vector3         m_Position;     //!< The position of the box.
public:
    //! Default constructor.
    QuadtreeNode();
    
    /*! \brief Constructor.
     *
     *  It is used to construct the box based on the specified parameters.
     *  \param _min         the height of the bottom face.
     *  \param _max         the height of the top face.
     *  \param pos          the position of the box.
     *  \param s            the width and the depth of the box.
     */
    QuadtreeNode(float _min, float _max, Vector2 pos, float s);

    /*! \brief Constructor.
     *
     *  Constructs a box that wraps around all children nodes.
     *  \param _children    list of all children nodes (4 in total).
     */
    QuadtreeNode(QuadtreeNode** _children);

    //! Destructor.
    ~QuadtreeNode();

    //! Returns the child that is at the specified index in the list.
    inline QuadtreeNode* getChild(size_t i) const { assert(i < 4); return m_Child[i]; }

    //! Returns the height of the center.
    inline float getAverage() const { return m_Position.y(); }

    //! Returns the height of the bottom face.
    inline float getMin() const { return m_Min; }

    //! Returns the height of the top face.
    inline float getMax() const { return m_Max; }

    //! Returns the width and the depth of box. They are equal, so just a single value is going to be returned.
    inline float size() const { return m_Size; }

    //! Returns the position of the quadtree node.
    inline Vector3 getPosition() const { return m_Position; }
};

//! Enumerates the types of stitching.
enum StitchingType
{
    TOP_STITCHING       = 1,        //! Stitching must be performed on the top (back) edge.
    RIGHT_STITCHING     = 1 << 1,   //! Stitching must be performed on the right edge.
    BOTTOM_STITCHING    = 1 << 2,   //! Stitching must be performed on the bottom (front) edge.
    LEFT_STITCHING      = 1 << 3    //! Stitching must be performed on the left edge.
};

//! Contains all information required for rendering a terrain patch.
class TerrainPatch
{
    friend class Terrain;

    //! Contains all delta values used for determining the level of detail.
    float           *m_Delta;

    //! Contains a mask used for determining the stitching.
    unsigned        m_PatchType;

    //! Contains the actual contents of the tile.
    VideoBufferPtr  m_Patch;

    float           m_Min,      //!< The height of the highest point.
                    m_Max;      //!< The height of the lowest point.
    Vector3         m_Position; //!< The position of the tile.
public:
    //! Constructor.
    TerrainPatch();

    //! Destructor.
     ~TerrainPatch();

    /*! \brief Builds all additional data required for rendering the patch.
     *  \param renderer         a pointer to rendering back-end.
     *  \param data             a reference to an array which contains the height map of this patch.
     */
    void build(const RendererPtr& renderer, const DynamicArray<Vector3>& data);

    //! Draws the patch by also using the common 
    void draw(Terrain& terrain);

    //! Returns the delta value at the specified level of detail.
    float getDelta(unsigned i) const { return m_Delta[i]; }

    //! Sets the level of detail.
    void setDetail(unsigned d) { m_PatchType = d << 4; }

    //! Returns the current level of detail.
    unsigned getDetail() const { return m_PatchType >> 4; }

    //! Sets the type of stitching.
    void stitching(unsigned s) { m_PatchType |= s; }

    //! Returns the position of this patch.
    Vector3 getPosition() const { return m_Position; }

    //! Returns the height of the highest point of this patch.
    float getMax() const { return m_Max; }

    //! Returns the height of the lowest point of this patch.
    float getMin() const { return m_Min; }
};

/*! \brief Terrain rendering.
 *  
 *  The main algorithm is based on geomipmapping with stitching.
 */
class Terrain: public Drawable
{
    //! Pointer to the rendering back-end.
    RendererPtr                     m_Renderer;

    //! The main state object used during the rendering of the terrain.
    StateObjectPtr                  m_State;

    //! The geometric size of each tile.
    float                           m_TileGeomSize;

    //! The size in vertices of each tile.
    unsigned                        m_PatchSize;

    //! The total amount of patches.
    unsigned                        m_PatchesCount;

    //! Used for specifying the size of the texture tile.
    float                           m_TexOffset;

    /*! \brief The threshold of the delta value which forces recalculation of the
     *         level of detail of all tiles.
     */
    float                           m_DeltaThreshold;

    //! The maximum of the highest value.
    float                           m_MaxHeight;

    //! The last position of the camera.
    Vector3                         m_LastCamPos;

    /*! \brief Contains the data split for each patch.
     *  \todo Replace or remove this array.
     */
    DynamicArray<VideoBufferPtr>    m_IndicesBuffer;

    //! Contains the indices for every level of detail and stitching.
    DynamicArray<unsigned>          m_IndicesSize;

    //! The array which contains all types of terrain tiles for the current map.
    TexturePtr                      m_Textures[3];

    //! Alpha map which determines in what ratio the samples from the different tiles must be mixed.
    TexturePtr                      m_Alphamap;

    //! Normal map which contains the normal vectors to the surface at each vertex.
    TexturePtr                      m_Normalmap;

    //! The quadtree used for collision queries and polygon culling.
    QuadtreeNode                    *m_Quadtree;

    //! Contains all patches.
    DynamicArray<TerrainPatch>      m_Patches;

    //! The minimum approximate pixel error that is allowed.
    float                           m_Tau;

    //! The size of the map.
    unsigned                        m_MapSize;

    //! The shading program used for rendering the terrain.
    ShaderProgramPtr                m_ShaderProg;

    //! The layout object used for reading the rendered data.
    InputLayoutPtr                  m_Layout;

    //! Pointer to the camera object.
    CameraPtr                       m_Camera;
public:
    //! Default constructor.
    Terrain();

    /*! \brief Constructor.
     *  \param cam          a pointer to the current camera.
     *  \param res_mgr      a reference to the resource manager.
     *  \param map_file     the file that contains the height map.
     *  \param splatting    the file that contains the alpha map.
     */
    Terrain(const CameraPtr& cam, const ResourceManagerPtr& res_mgr, const string& map_file, const string& splatting);

    //! Destructor.
    virtual ~Terrain();

    /*! \brief Initializes the terrain.
     *  \param cam          a pointer to the current camera.
     *  \param res_mgr      a reference to the resource manager.
     *  \param map_file     the file that contains the height map.
     *  \param splatting    the file that contains the alpha map.
     */
    void init(const CameraPtr& cam, const ResourceManagerPtr& res_mgr, const string& map_file, const string& splatting);

    //! Sets the current camera.
    void setCamera(const CameraPtr& cam);

    /*! \brief Renders the terrain.
     *  \param scene        a reference to the scene object which is the parent of this object.
     *  \param transform    the matrix that describes where the terrain should be rendered.
     */
    virtual void draw(Scene& scene, const Matrix4& transform);

    //! 
    float getTau() const { return m_Tau; }

    //! Returns the size of the texture tile.
    float getTextureOffset() const { return m_TexOffset; }

    //! Returns the highest point of the terrain.
    float getMaxHeight() const { return m_MaxHeight; }

    //! Returns the current size of each patch.
    unsigned getPatchSize() const { return m_PatchSize; }

    //! Returns the geometric size of the geometric tile.
    float getTileGeomSize() const { return m_TileGeomSize; }

    //! Returns the appropriate indices for the specified type of patch.
    VideoBufferPtr getIndicesBuffer(unsigned patch_type) const { return m_IndicesBuffer[patch_type]; }

    //! Returns the amount of indices for the specified patch.
    unsigned getIndicesSize(unsigned patch_type) const { return m_IndicesSize[patch_type]; }

    //! Returns a pointer to rendering back-end.
    RendererPtr getRenderer() { return m_Renderer; }

private:
    //! Recalculates the delta values.
    void recalculateDelta();

    //! Generates the normal map based on the specified height map.
    void generateNormalmap(const DynamicArray<float>& heightmap);

    //! Generates the terrain data based on the specified height map.
    void generateTerrainData(const DynamicArray<float>& heightmap);

    //! Generates the appropriate indices for each level of detail.
    void generateIndices();

	CE_NO_ATTRIBUTES(CE_POLYMORPHIC, Drawable)
};
}

#endif /* TERRAIN_HH_ */
